from config import *
from common import *
import os
import random
import shutil
import socket
import sqlite3
import time
from cStringIO import StringIO
from hashlib import md5
from twisted.internet import reactor, threads
from twisted.enterprise import adbapi       
from twisted.python.util import println
import cPickle   
max_num_blocks_get = 1000 #max number of blocks requested from tracker at once
max_repeat = 100 #max number of recipes a block can be used for before being discarded
max_num_blocks = 1 << 20 #max number of blocks managed by the monitor
class Monitor:
    blocks = {}
    '''bid->[num_used, peer[]]'''
    crypto_db = 'monitor/crypto.sqlite'
    crypto_schema_file = 'monitor/crypto.sql'
    def __init__(self,
                 http_host=MONITOR_HTTP_HOST,
                 http_port=MONITOR_HTTP_PORT,
                 rpc_host=MONITOR_RPC_HOST,
                 rpc_port=MONITOR_RPC_PORT,
                 external_ip=MONITOR_EXTERNAL_IP,
                 external_port=MONITOR_EXTERNAL_PORT,
                 tracker_host=TRACKER_EXTERNAL_IP,
                 tracker_port=TRACKER_EXTERNAL_PORT):            
        self.http_host = http_host
        self.http_port = http_port
        self.rpc_host = rpc_host
        self.rpc_port = rpc_port
        self.external_ip = external_ip
        self.external_port = external_port
        check_schema(self.crypto_db, self.crypto_schema_file)        
        crypto.generate_keys(self.crypto_db)
        self.pubkey = crypto.get_public_key(self.crypto_db)
        self.tracker = RPCClient(tracker_host, tracker_port, ping_interval=5)
        set_public_key(self.tracker)
        self.rpc_server = RPCServer(self)
        self.get_blocks_from_tracker()
    #give a peer a list of under seeded blocks, so that peer can share them.
    ###RPC METHODS####
    def get_public_key(self):
        print "get_public_key"
        return self.pubkey
    def get_unused_blocks(self, num_blocks=max_num_blocks_get):
        print "get_unused_blocks"
        print "# current blocks = ", len(self.blocks)
        print "# requested blocks = ", num_blocks
        num_blocks = min(num_blocks, len(self.blocks))
        ret = {}                
        for i in xrange(num_blocks):
            #randomly choose which blocks the peer should reuse
            bid = random.choice(self.blocks.keys()) 
            self.blocks[bid][0] += 1
            if self.blocks[bid][0] > max_repeat:
                del self.blocks[bid]
            ret[bid] = self.blocks[bid][1]
        return ret
    
    ##METHOD TO GET BLOCKS FROM TRACKER PERIODICALLY##
    def get_blocks_from_tracker(self):
       print "get_blocks_from_tracker"
       if not self.tracker.status:
           reactor.callLater(5, self.get_blocks_from_tracker)
           return
       if len(self.blocks) >= max_num_blocks:
           reactor.callLater(30, self.get_blocks_from_tracker)
           return
       
       d = self.tracker.get_unused_blocks(max_num_blocks_get)
       d.addErrback(errorHandler("problem during tracker.get_unused_blocks"))
       def handle_deferred(d):
           print "handle_deferred"
           d.addCallback(lambda m:self.blocks.update(m) and println("# blocks from tracker =%d" % len(m)) )
           reactor.callLater(10, self.get_blocks_from_tracker)
       d.addCallback(handle_deferred)
       d.addErrback(errorHandler("problem during handle_deferred"))
       
           
        